package cloud.xlh.my_system.listener;

import cloud.xlh.infrastructure.cache.operate.CacheOperateService;
import cloud.xlh.infrastructure.utils.FileUtil;
import cloud.xlh.infrastructure.utils.StringUtil;
import cloud.xlh.my_system.dynamic_env_refresh.CacheData;
import cloud.xlh.my_system.event.ServiceCallChangeEvent;
import cloud.xlh.my_system.in.ServiceIn;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.http.*;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.StringJoiner;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created with IntelliJ IDEA.
 *
 * @ project name : xlh_osms
 * @ Author: XuLeHuang
 * @ Date: 2023/3/21 14:29
 * @ Description:
 */
@Slf4j
@Component
public class ServiceCallChangeEventListener {
    @Resource(name = "Caffeine")
    private CacheOperateService caffeineCacheOperateService;
    @Resource(name = "Redis")
    private CacheOperateService redisCacheOperateService;

    private static final ConcurrentHashMap<String, Method> concurrentHashMap = new ConcurrentHashMap<>();
    private static final String serviceCallListFileLocation = "D:\\development\\environment\\idea\\IdeaProjects\\xlh_osms\\module\\my-system\\src\\main\\resources\\serviceCallList.json";

    public ServiceCallChangeEventListener() {
        Method[] declaredMethods = ServiceIn.class.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            //ServiceMapping要用注解处理器那个，但是那个是编译时期的，而反射是运行时，所以拿不到
            //ServiceMapping serviceMapping = declaredMethod.getDeclaredAnnotation(ServiceMapping.class);
            String methodName = declaredMethod.getName();
            int length = methodName.length();
            String path = methodName.substring(length - 4, length);
            concurrentHashMap.put(path, declaredMethod);
        }
    }

    @SneakyThrows
    @EventListener(ServiceCallChangeEvent.class)
    @Async
    public void sendMsg(ServiceCallChangeEvent event) {
        long start = System.nanoTime();
        pullChangedConfig();
        HashMap<String, Method> pathMethodMap = new HashMap<>();
        if (pathMethodMap != null) {
            String changeServiceStr = event.getChangeServiceStr();

            //1.远程通讯
            //发送http请求，拉取待更新配置
            ResponseEntity<String> responseEntity = pullChangedConfig();

            String remoteConfig = "";
            if (HttpStatus.OK.value() == responseEntity.getStatusCodeValue()) {
                remoteConfig = responseEntity.getBody();
                ConcurrentHashMap<String, CacheData> cacheMap = event.getCacheMap();
                CacheData cacheData = cacheMap.get("serviceCallList");
                cacheData.setValue(remoteConfig);
            }

            //2.更新本地缓存
            refreshLocalCache(responseEntity , event , pathMethodMap , remoteConfig);

            //3.更新分布式缓存
            log.info("开始异步更新分布式缓存");
            //redisCacheOperateService.put("pathMethodMap", pathMethodMap);

            //4.更新分布式缓存
            refreshLocalFile(remoteConfig);

            long end = System.nanoTime();
            log.info("{}：服务配置刷新耗时：({})毫秒", changeServiceStr, (end - start));
        }
    }

    /**
     *
     * @param remoteConfig
     * @return void
     * @author XuLeHuang
     * @create 2023/3/25
     * @name:
     * @Description: 更新分布式缓存
     **/
    private void refreshLocalFile(String remoteConfig) {
        log.info("异步更新本地服务配置文件");
        StringBuilder configStringBuilder = new StringBuilder("\"svcCode\":");
        configStringBuilder = configStringBuilder.append("\"" + remoteConfig + "\"");
        StringJoiner stringJoiner = new StringJoiner("", "{", "}");
        String configStr = stringJoiner.add(configStringBuilder.toString()).toString();
        FileUtil.writeFileByOverride(configStr , serviceCallListFileLocation , null);
    }

    /**
     *
     * @param responseEntity
     * @param event
     * @param pathMethodMap
     * @param remoteConfig
     * @return void
     * @author XuLeHuang
     * @create 2023/3/25
     * @name:
     * @Description: 更新本地缓存
     **/
    private void refreshLocalCache(ResponseEntity<String> responseEntity , ServiceCallChangeEvent event , HashMap<String, Method> pathMethodMap , String remoteConfig) {
        log.info("开始更新本地缓存");
        String[] servicePathArray = remoteConfig.split(",");
        for (int i = 0; i < servicePathArray.length; i++) {
            Method method = concurrentHashMap.get(servicePathArray[i]);
            if (method != null) {
                pathMethodMap.put(servicePathArray[i], method);
            }
        }
        log.info("pathMethodMap->{}" , pathMethodMap);
        caffeineCacheOperateService.put("pathMethodMap", pathMethodMap);
    }

    /**
     *
     * @return org.springframework.http.ResponseEntity<java.lang.String>
     * @author XuLeHuang
     * @create 2023/3/25
     * @name:
     * @Description: 发送http请求，拉取待更新配置
     **/
    private ResponseEntity<String> pullChangedConfig() {
        log.info("发送http请求，拉取待更新配置");
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        HttpEntity<Object> request = new HttpEntity<>(httpHeaders);
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> responseEntity = restTemplate.exchange("http://localhost:9090/v1/cs/configs/getChangeConfig", HttpMethod.GET, request, String.class);
        System.out.println(responseEntity);
        if (!StringUtil.isEmpty(responseEntity.getBody())) {
            return responseEntity;
        } else {
            return null;
        }
    }
}
